package business;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import util.Globals;
import exception.NoCodeFileFoundException;
import exception.NoNetLogoCodeFoundToGenerateException;
import exception.NoNetLogoOthersPartsOfApplicationFoundToGenerateException;
import exception.NoOthersPartsOfApplicationOnFileImportedException;

public class FileIOManipulate {
	private FileReader fileReader;
	private BufferedReader bufferReader;

	private String fileGeneratePath;
	private FileWriter fileWriter;
	private BufferedWriter bufferedWriter;
	private PrintWriter printWriter; 

	public FileIOManipulate(String fileReaderPath, String fileWriterPath) throws IOException{
		fileReader = new FileReader(fileReaderPath);
		bufferReader = new BufferedReader(fileReader);

		fileGeneratePath = fileWriterPath;
	}

	/**
	 * Reads the imported file:
	 * -Sets the netlogo code list;
	 * -Sets the other parts of application list.
	 * 
	 * @param fileToRead
	 * @throws IOException
	 */
	public void readImportedFile(FileComponent fileToRead) throws IOException{
		String actualLine = "";
		
		/* This variable contents the string that separate the parts of the netlogo code. */ 
		String breakNetLogoCode = Globals.BREAK_NETLOGO_CODE;

		boolean breakNetLogoCodeFound = false;

		int filePosition = 0;
		/* While exists code on file: read. */
		while((actualLine = bufferReader.readLine())!=null){
			if(actualLine.equals(breakNetLogoCode)){
				breakNetLogoCodeFound = true;
			}
			boolean hasComment = Globals.hasCommentInLine(actualLine);
			boolean hasFunctionDeclaration = Globals.hasFunctionDeclarationInLine(actualLine);
			/* Creates a line with its fields. */
			Line lineToAdd = new Line(filePosition, actualLine, hasComment);

			if(!breakNetLogoCodeFound){
				/* Puts the netlogo code line on netlogo code list */
				fileToRead.getNetlogoCode().getCodeLines().add(lineToAdd);
				if(hasFunctionDeclaration){
					/* Puts the function declaration line on function declaration list */
					fileToRead.getNetlogoCode().getNetlogoLinesFunctionNames().add(lineToAdd);
				}
			}else{
				/* Puts the other parts of application code line on other parts of application list */
				fileToRead.getNetlogoOthersPartsOfApplicantion().add(lineToAdd);
			}
			filePosition++;
		}
	}


	/**
	 * For each line from a code file, read and prints on console.
	 * 
	 * @param fileComponent
	 * @throws NoCodeFileFoundException
	 */
	public void printCodeFile(FileComponent fileComponent) throws NoCodeFileFoundException{
		boolean hasFileCode = false;
		if(!fileComponent.getNetlogoCode().getCodeLines().isEmpty()){
			for(Line codeLines : fileComponent.getNetlogoCode().getCodeLines()){
				System.out.println(codeLines.getValue());
			}
			hasFileCode = true;
		}

		if(!hasFileCode){
			throw new NoCodeFileFoundException(fileComponent.getPath());
		}
	}

	/**
	 * For each line from all netlogo code and parts of application file, read and prints on console.
	 * 
	 * @param fileComponent
	 * @throws NoCodeFileFoundException
	 * @throws NoOthersPartsOfApplicationOnFileImportedException
	 */
	public void printCompleteFile(FileComponent fileComponent) throws NoCodeFileFoundException, NoOthersPartsOfApplicationOnFileImportedException{
		printCodeFile(fileComponent);

		boolean hasOthersPartsOfApplication = false;
		if(!fileComponent.getNetlogoCode().getCodeLines().isEmpty()){
			for(Line codeLines : fileComponent.getNetlogoCode().getCodeLines()){
				System.out.println(codeLines.getValue());
			}
			hasOthersPartsOfApplication = true;
		}

		if(!hasOthersPartsOfApplication){
			throw new NoOthersPartsOfApplicationOnFileImportedException(fileComponent.getPath());
		}
	}

	/**
	 * Create file with the joined code (netlogo + aspect) and the others parts of application.
	 * 
	 * @throws IOException
	 * @throws NoNetLogoOthersPartsOfApplicationFoundToGenerateException 
	 * @throws NoNetLogoCodeFoundToGenerateException 
	 */
	public void generateFile(FileComponent fileComponent) throws IOException, NoNetLogoOthersPartsOfApplicationFoundToGenerateException, NoNetLogoCodeFoundToGenerateException{
		fileWriter = new FileWriter(fileGeneratePath);
		bufferedWriter = new BufferedWriter(fileWriter);
		printWriter = new PrintWriter(bufferedWriter);

		if(!fileComponent.getNetlogoCode().getCodeLines().isEmpty()){
			/* Reads the netlogo code list and puts on file. */
			for(Line lineCode : fileComponent.getNetlogoCode().getCodeLines()){
				printWriter.println(lineCode.getValue()); 
			}

			if(!fileComponent.getNetlogoOthersPartsOfApplicantion().isEmpty()){
				/* Reads the other parts of application list and puts on file. */
				for(Line lineCode : fileComponent.getNetlogoOthersPartsOfApplicantion()){
					printWriter.println(lineCode.getValue()); 
				}
			}else{
				throw new NoNetLogoOthersPartsOfApplicationFoundToGenerateException();
			}
		}else{
			throw new NoNetLogoCodeFoundToGenerateException();
		}

		/* Finalize the generated file. */ 
		printWriter.close();
		bufferedWriter.close();
		fileWriter.close();
	}

	public FileReader getFileReader() {
		return fileReader;
	}

	public void setFileReader(FileReader fileReader) {
		this.fileReader = fileReader;
	}

	public BufferedReader getBufferReader() {
		return bufferReader;
	}

	public void setBufferReader(BufferedReader bufferReader) {
		this.bufferReader = bufferReader;
	}

	public FileWriter getFileWriter() {
		return fileWriter;
	}

	public void setFileWriter(FileWriter fileWriter) {
		this.fileWriter = fileWriter;
	}

	public BufferedWriter getBufferedWriter() {
		return bufferedWriter;
	}

	public void setBufferedWriter(BufferedWriter bufferedWriter) {
		this.bufferedWriter = bufferedWriter;
	}

	public PrintWriter getPrintWriter() {
		return printWriter;
	}

	public void setPrintWriter(PrintWriter printWriter) {
		this.printWriter = printWriter;
	}

	public String getFileGeneratePath() {
		return fileGeneratePath;
	}

	public void setFileGeneratePath(String fileGeneratePath) {
		this.fileGeneratePath = fileGeneratePath;
	}

}
